/*
Copyright (C) BABEC. All rights reserved.

SPDX-License-Identifier: Apache-2.0
*/
/*
参照CMEVI合约标准实现：
https://git.chainmaker.org.cn/contracts/standard/-/blob/master/draft/CM-CS-221221-Evidence.md
*/

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"chainmaker.org/chainmaker/contract-sdk-go/v2/pb/protogo"
	"chainmaker.org/chainmaker/contract-sdk-go/v2/sandbox"
	"chainmaker.org/chainmaker/contract-sdk-go/v2/sdk"
	"chainmaker.org/chainmaker/contract-utils/str"
)

const (
	paramId       = "id"
	paramHash     = "hash"
	paramMetadata = "metadata"

	methodPreEvidence          = "[evidence]"
	methodPreExistsOfHash      = "[existsOfHash]"
	methodPreExistsOfId        = "[existsOfId]"
	methodPreFindByHash        = "[findByHash]"
	methodPreFindById          = "[findById]"
	methodPreExistsOfIdAndHash = "[existsOfIdAndHash]"
	methodPreUpdateEvidence    = "[updateEvidence]"
	methodPreFindHisById       = "[findHisById]"

	TrueString  = "true"
	FalseString = "false"
)

// Evidence 存证结构体
type Evidence struct {
	// Id 业务流水号
	Id string `json:"id"`
	// Hash 哈希值
	Hash []string `json:"hash"`
	// TxId 存证时交易ID
	TxId string `json:"txId"`
	// BlockHeight 存证时区块高度
	BlockHeight int `json:"blockHeight"`
	// Timestamp 存证时区块时间
	Timestamp string `json:"timestamp"`
	// Metadata 可选，其他信息；具体参考下方 Metadata 对象。
	Metadata string `json:"metadata"`
}

// Metadata 可选信息建议字段，若包含以下相关信息存证，请采用以下字段
type Metadata struct {
	// HashType 哈希的类型，文字、文件、视频、音频等
	HashType string `json:"hashType"`
	// HashAlgorithm 哈希算法，sha256、sm3等
	HashAlgorithm string `json:"hashAlgorithm"`
	// Username 存证人，用于标注存证的身份
	Username string `json:"username"`
	// 存证内容
	Content string `json:"content"`
	// 存证状态
	Status string `json:"status"`
	// ...
}

// EvidenceContract 存证合约实现
type EvidenceContract struct {
}

// InitContract install contract func
func (e *EvidenceContract) InitContract() protogo.Response {
	return sdk.SuccessResponse
}

// UpgradeContract upgrade contract func
func (e *EvidenceContract) UpgradeContract() protogo.Response {
	return sdk.SuccessResponse
}

// InvokeContract the entry func of invoke contract func
func (e *EvidenceContract) InvokeContract(method string) (result protogo.Response) {
	// 记录异常结果日志
	defer func() {
		if result.Status != 0 {
			sdk.Instance.Warnf(result.Message)
		}
	}()

	switch method {
	case "Evidence":
		return e.evidenceCore()
	case "ExistsOfId":
		return e.existsOfIdCore()
	case "ExistsOfIdAndHash":
		return e.existsOfIdAndHashCore()
	case "FindById":
		return e.findByIdCore()
	case "UpdateEvidence":
		return e.updateEvidenceCore()
	case "FindHisById":
		return e.findHisByIdCore()
	default:
		return sdk.Error("invalid method")
	}
}

func (e *EvidenceContract) evidenceCore() protogo.Response {
	params := sdk.Instance.GetArgs()

	// 获取参数
	id := string(params[paramId])
	hash := string(params[paramHash])
	metadata := string(params[paramMetadata])
	if str.IsAnyBlank(id) {
		return e.error(methodPreEvidence, "id is empty")
	}
	var hashStr string
	if len(hash) == 0 {
		return e.error(methodPreEvidence, "hash is empty")
	} else {
		hashStr = string(hash)
	}
	hashes := strings.Split(hashStr, ",")
	// 执行逻辑
	err := e.Evidence(id, hashes, metadata)
	if err != nil {
		return e.error(methodPreEvidence, err.Error())
	}

	// 返回OK
	return sdk.SuccessResponse
}
func (e *EvidenceContract) updateEvidenceCore() protogo.Response {
	params := sdk.Instance.GetArgs()

	// 获取参数
	id := string(params[paramId])
	hash := string(params[paramHash])
	metadata := string(params[paramMetadata])
	if str.IsAnyBlank(id, hash) {
		return e.error(methodPreUpdateEvidence, "id is empty")
	}
	var hashStr string
	if len(hash) == 0 {
		return e.error(methodPreUpdateEvidence, "hash is empty")
	} else {
		hashStr = string(hash)
	}
	hashes := strings.Split(hashStr, ",")
	// 执行逻辑
	err := e.UpdateEvidence(id, hashes, metadata)
	if err != nil {
		return e.error(methodPreUpdateEvidence, err.Error())
	}
	return sdk.SuccessResponse
}

func (e *EvidenceContract) UpdateEvidence(id string, hash []string, metadata string) error {
	// 校验id是否存在
	exist, err := e.ExistsOfId(id)
	if err != nil {
		return errors.New(methodPreUpdateEvidence + "can not find id:" + id + err.Error())
	}
	if !exist {
		return errors.New("id is not exists: " + id)
	}

	// 获取区块信息, 构建存证对象
	txId, _ := sdk.Instance.GetTxId()
	blockHeight, _ := sdk.Instance.GetBlockHeight()
	txTimeStamp, _ := sdk.Instance.GetTxTimeStamp()

	evidence := Evidence{
		Id:          id,
		TxId:        txId,
		Hash:        hash,
		BlockHeight: blockHeight,
		Timestamp:   txTimeStamp,
		Metadata:    metadata,
	}

	data, err := json.Marshal(&evidence)
	if err != nil {
		return errors.New(methodPreUpdateEvidence + "marshal evidence failed:" + err.Error())
	}
	err = sdk.Instance.PutStateFromKeyByte(id, data)
	if err != nil {
		return errors.New(methodPreUpdateEvidence + "put state from key failed:" + err.Error())
	}

	return nil
}

func (e *EvidenceContract) findHisByIdCore() protogo.Response {
	params := sdk.Instance.GetArgs()

	id := string(params[paramId])
	if str.IsAnyBlank(id) {
		return e.error(methodPreFindHisById, "id is empty")
	}
	evidences, err := e.FindHisById(id)
	if err != nil {
		return e.error(methodPreFindHisById, err.Error())
	}

	return sdk.Success(evidences)
}

func (e *EvidenceContract) FindHisById(id string) ([]byte, error) {
	iter, err := sdk.Instance.NewHistoryKvIterForKey(id, "")
	if err != nil {
		errMsg := fmt.Sprintf("new HistoryKvIter for key=[%s] failed, %s", id, err)
		return nil, errors.New(errMsg)
	}
	evidences := make([]Evidence, 0)
	var TempEvidence *Evidence
	for iter.HasNext() {
		km, err := iter.Next()
		if err != nil {
			errMsg := "iterator failed to get the next element" + "," + err.Error()
			sdk.Instance.Errorf(errMsg)
			// 避免出现EOF，暂时跳过
			continue
		}
		err = json.Unmarshal(km.Value, &TempEvidence)
		if err != nil {
			errMsg := "json parse element error" + "," + err.Error()
			sdk.Instance.Errorf(errMsg)
			continue
		}
		evidences = append(evidences, *TempEvidence)
	}
	closed, err := iter.Close()
	if !closed || err != nil {
		errMsg := fmt.Sprintf("iterator close failed, %s", err.Error())
		sdk.Instance.Errorf(errMsg)
		return nil, errors.New(errMsg)
	}
	data, err := json.Marshal(evidences)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func containsAllMap(target, container []string) bool {
	// 创建一个map用于快速查找
	containerMap := make(map[string]bool)
	for _, c := range container {
		containerMap[c] = true
	}

	// 检查target中的每个元素是否都在map中
	for _, t := range target {
		if !containerMap[t] {
			return false
		}
	}
	return true
}

func (e *EvidenceContract) existsOfIdAndHashCore() protogo.Response {
	params := sdk.Instance.GetArgs()

	// 获取、解析参数
	id := string(params[paramId])
	hash := string(params[paramHash])
	if str.IsAnyBlank(id, hash) {
		return e.error(methodPreExistsOfIdAndHash, "id or hash is empty")
	}
	findEv, err := e.FindById(id)
	if err != nil {
		return e.error(methodPreExistsOfIdAndHash, err.Error())
	}
	var hashStr string
	if len(hash) == 0 {
		return e.error(methodPreUpdateEvidence, "hash is empty")
	} else {
		hashStr = string(hash)
	}
	hashes := strings.Split(hashStr, ",")
	if !containsAllMap(hashes, findEv.Hash) {
		return sdk.Success([]byte(FalseString))
	}
	return sdk.Success([]byte(TrueString))
}

func (e *EvidenceContract) existsOfIdCore() protogo.Response {
	params := sdk.Instance.GetArgs()

	id := string(params[paramId])
	if str.IsAnyBlank(id) {
		return e.error(methodPreExistsOfId, "id is empty")
	}

	if exists, err := e.ExistsOfId(id); err != nil {
		return e.error(methodPreExistsOfId, err.Error())
	} else if exists {
		return sdk.Success([]byte(TrueString))
	} else {
		return sdk.Success([]byte(FalseString))
	}
}

func (e *EvidenceContract) findByIdCore() protogo.Response {
	params := sdk.Instance.GetArgs()

	id := string(params[paramId])
	if str.IsAnyBlank(id) {
		return e.error(methodPreFindById, "id is empty")
	}

	evidence, err := e.FindById(id)
	if err != nil {
		return e.error(methodPreFindById, err.Error())
	}

	data, err := json.Marshal(evidence)
	if err != nil {
		return e.error(methodPreFindById, err.Error())
	}
	return sdk.Success(data)
}

// Evidence 存证
func (e *EvidenceContract) Evidence(id string, hash []string, metadata string) error {
	// 校验是否存在
	existsId, err := sdk.Instance.GetStateFromKey(id)
	if err != nil {
		return errors.New(methodPreEvidence + "get state from key failed:" + err.Error())
	}
	if !str.IsAnyBlank(existsId) {
		return errors.New(methodPreEvidence + "id already exists: " + id)
	}

	// 获取区块信息, 构建存证对象
	txId, _ := sdk.Instance.GetTxId()
	blockHeight, _ := sdk.Instance.GetBlockHeight()
	txTimeStamp, _ := sdk.Instance.GetTxTimeStamp()
	sdk.Instance.Infof("Evidence=", hash)
	evidence := Evidence{
		Id:          id,
		TxId:        txId,
		Hash:        hash,
		BlockHeight: blockHeight,
		Timestamp:   txTimeStamp,
		Metadata:    metadata,
	}

	data, err := json.Marshal(&evidence)
	if err != nil {
		return errors.New(methodPreEvidence + "marshal evidence failed:" + err.Error())
	}
	err = sdk.Instance.PutStateFromKeyByte(id, data)
	if err != nil {
		return errors.New(methodPreEvidence + "put state from key failed:" + err.Error())
	}

	return nil
}

// ExistsOfId ID是否存在
func (e *EvidenceContract) ExistsOfId(id string) (bool, error) {
	existsId, err := sdk.Instance.GetStateFromKeyByte(id)
	return len(existsId) > 0, err
}

// FindById 根据id查找
func (e *EvidenceContract) FindById(id string) (*Evidence, error) {
	evidenceByte, err := sdk.Instance.GetStateFromKeyByte(id)
	if err != nil {
		return nil, err
	}
	if len(evidenceByte) == 0 {
		return nil, errors.New("evidence not found by id: " + id)
	}
	evi := &Evidence{}
	err = json.Unmarshal(evidenceByte, evi)
	return evi, err
}

// EvidenceBatch 批量存证
func (e *EvidenceContract) EvidenceBatch(evidences []Evidence) error {
	for i := range evidences {
		err := e.Evidence(evidences[i].Id, evidences[i].Hash, evidences[i].Metadata)
		if err != nil {
			return err
		}
	}
	return nil
}

func (e *EvidenceContract) error(methodPre, error string) protogo.Response {
	return sdk.Error(methodPre + "method invoke fail, error: " + error)
}

func main() {
	err := sandbox.Start(new(EvidenceContract))
	if err != nil {
		sdk.Instance.Errorf(err.Error())
	}
}
